<?php
/**
 * 周舟 hzboye010@163.com
 * addby sublime snippets
 */

namespace Api\Domain;

use Admin\Api\AlipayNotifyApi;
use Admin\Api\OrdersPaycodeApi;
use Shop\Api\OrdersApi;
use Shop\Model\OrdersModel;
use Think\Controller\RestController;
use Admin\Api\ConfigApi;

class WxpayDomain extends BaseDomain {

    protected $business_code = 'wxpay';
    protected $config = [];
    protected $allowType = array("json", "rss", "html");

    /**
     * 准备支付
     */
    public function preOrder(){
      // return 'preORder';//exit;
      $this->getWxpayConfig();

      $this->checkVersion(100);

      addLog("wxpay/prePay",$_GET,$_POST,"应用" . $this->client_id . "调用微信预支付接口");

      $code = $this->_post('code','','缺失支付code');
      $paytype = substr($code,0,1);
if($paytype == 'T'){
      $p_pos = strpos($code,"P");
      $pay_id = substr($code,$p_pos+1,strlen($code)-$p_pos);
      $result = apiCall(OrdersPaycodeApi::GET_INFO,array(array('id'=>$pay_id)));
      if($result['status'] && is_array($result['info'])){
        $orderCodeArray=explode(';',$result['info']['order_content']);
      }else{
        $this->apiReturnErr('查无此单');
      }
      $order_code = $orderCodeArray[0];

      //查询订单
      $result = apiCall(OrdersApi::GET_INFO,array(array('order_code'=>$order_code)));
      if(!$result['status']){
         LogRecord($result['info'],__FILE__.__LINE__);
      }
      // dump($result);exit;
      $order_info = $result['info'];
      if(is_null($order_info)){
         $this->apiReturnErr('无此订单');
      }
      if($order_info['pay_status'] >0){
        $this->apiReturnErr('订单已付款或退款');
      }
      //写入pay_code
      $result = apiCall(OrdersApi::SAVE,array(array('order_code'=>$order_code),array('pay_code'=>$code)));
      // dump($order_info);exit;
      $tade_no   = $code;//$order_info['order_code'];//pay_code
      $orderBody = $order_info['note']? $order_info['note']:$tade_no;
      $total_fee = $order_info['price']*100; //订单总金额，单位为分
      // echo $total_fee;exit;
}elseif( $paytype=='S' ){
  $tade_no  = $code;
  $orderBody = $this->_post('desc','[积分商品微信支付]'.$code);
  $fee = $this->_post('total_price','','缺失订单金额');
  $total_fee = floatval($fee)*100;
}else{
  $this->apiReturnErr('未知交易类型');
}
      $r = $this->getPrePayOrder($orderBody, $tade_no, $total_fee);
      // dump($r);exit;
      if($r['return_code'] == 'FAIL'){
       $this->apiReturnErr('微信后台:'.$r['return_msg']);
      }elseif($r['return_code'] == 'SUCCESS'){
       if($r['result_code'] == 'SUCCESS'){
         $this->apiReturnSuc($this->getOrder($r['prepay_id']));
       }else{
         $this->apiReturnErr('微信后台:'.$r['err_code_des']); 
       }
      }else{
       $this->apiReturnErr('微信后台:返回错误');
      }

      // $result = apiCall(WxpayAppOrdersApi::GET_INFO,array(array('order_code'=>$order_code)));
      // if($result['status']){
      //       $this->apiReturnSuc($result['info']);
      // }else{
      //       $this->apiReturnErr($result['info']);
      // }
    }

    protected function getPrePayOrder($body, $out_trade_no, $total_fee){
      $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
      $notify_url = $this->config["notify_url"];

      $onoce_str = $this->getRandChar(32);

      $data["appid"]            = $this->config["appid"];
      $data["body"]             = $body;
      $data["mch_id"]           = $this->config['mch_id'];
      $data["nonce_str"]        = $onoce_str;
      $data["notify_url"]       = $notify_url;
      $data["out_trade_no"]     = $out_trade_no;
      $data["spbill_create_ip"] = $this->get_client_ip();
      $data["total_fee"]        = $total_fee;
      $data["trade_type"]       = "APP";
      $s = $this->getSign($data,false);
      $data["sign"] = $s;
      //生成pay_code
      // $pay_code =  $this->getPayCode(1);
      // return $data;

      $xml = $this->arrayToXml($data);
      $response = $this->postXmlCurl($xml, $url);

      //将微信返回的结果xml转成数组
      return $this->xmlstr_to_array($response);
    }

  //执行第二次签名，才能返回给客户端使用
  protected function getOrder($prepayId){
    $data["appid"] = $this->config["appid"];
    $data["noncestr"] = $this->getRandChar(32);;
    $data["package"] = "Sign=WXPay";
    $data["partnerid"] = $this->config['mch_id'];
    $data["prepayid"] = $prepayId;
    $data["timestamp"] = time();
    $s = $this->getSign($data, false);
    $data["sign"] = $s;
    $data["packageValue"] = "Sign=WXPay";

    return $data;
  }
     /**
     * 从数据库中取得微信配置信息
     */
    protected function getWxpayConfig() {
        // $config = S('global_config');
        //dump($config);
        // if ($config === false) {
            $map = array('name'=>'WXPAY_CONFIG');
            $fields = 'type,name,value';
            $result = apiCall(ConfigApi::GET_INFO, array($map, false, $fields));
            if ($result['status']) {
              $value = $result['info'];
              $this->config = $this -> parse($value['type'], $value['value']);

            } else {
                LogRecord('INFO:' . $result['info'], '[FILE] ' . __FILE__ . ' [LINE] ' . __LINE__);
                $this -> error($result['info']);
            }
        // }
        // C($config);
    }

    /**
     * 根据配置类型解析配置
     * @param  integer $type 配置类型
     * @param  string $value 配置值
     * @return array|string
     */
    private static function parse($type, $value) {
      switch ($type) {
        case 3 :
          //解析数组
          $array = preg_split('/[,;\r\n]+/', trim($value, ",;\r\n"));
          if (strpos($value, ':')) {
            $value = array();
            foreach ($array as $val) {
              list($k, $v) = explode(':', $val,2);
              $value[$k] = $v;
            }
          } else {
            $value = $array;
          }
          break;
      }
      return $value;
    }

       /*
        生成签名
    */
    protected function getSign($Obj)
    {
        foreach ($Obj as $k => $v)
        {
            $Parameters[strtolower($k)] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo "【string】 =".$String."</br>";
        //签名步骤二：在string后加入KEY
        $String = $String."&key=".$this->config['api_key'];
        // echo "<textarea style='width: 50%; height: 150px;'>$String</textarea> <br />";
        //签名步骤三：MD5加密
        $result_ = strtoupper(md5($String));
        return $result_;
    }

    //获取指定长度的随机字符串
    protected function getRandChar($length){
       $str = null;
       $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
       $max = strlen($strPol)-1;

       for($i=0;$i<$length;$i++){
        $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
       }

       return $str;
    }

    //数组转xml
    protected function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
             if (is_numeric($val))
             {
                $xml.="<".$key.">".$val."</".$key.">";

             }
             else
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml.="</xml>";
        return $xml;
    }

    //post https请求，CURLOPT_POSTFIELDS xml格式
    protected function postXmlCurl($xml,$url,$second=30)
    {
        //初始化curl
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data)
        {
            curl_close($ch);
            return $data;
        }
        else
        {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error"."<br>";
            echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            curl_close($ch);
            return false;
        }
    }

    /*
        获取当前服务器的IP
    */
    protected function get_client_ip()
    {
        if ($_SERVER['REMOTE_ADDR']) {
        $cip = $_SERVER['REMOTE_ADDR'];
        } elseif (getenv("REMOTE_ADDR")) {
        $cip = getenv("REMOTE_ADDR");
        } elseif (getenv("HTTP_CLIENT_IP")) {
        $cip = getenv("HTTP_CLIENT_IP");
        } else {
        $cip = "unknown";
        }
        return $cip;
    }

    //将数组转成uri字符串
    protected function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
            if($urlencode)
            {
               $v = urlencode($v);
            }
            $buff .= strtolower($k) . "=" . $v . "&";
        }
        $reqPar;
        if (strlen($buff) > 0)
        {
            $reqPar = substr($buff, 0, strlen($buff)-1);
        }
        return $reqPar;
    }

    /**
     *xml转成数组
    */
    protected function xmlstr_to_array($xmlstr) {
      $doc = new \DOMDocument();
      $doc->loadXML($xmlstr);
      return $this->domnode_to_array($doc->documentElement);
    }
    protected function domnode_to_array($node) {
      $output = array();
      switch ($node->nodeType) {
       case XML_CDATA_SECTION_NODE:
       case XML_TEXT_NODE:
        $output = trim($node->textContent);
       break;
       case XML_ELEMENT_NODE:
        for ($i=0, $m=$node->childNodes->length; $i<$m; $i++) {
         $child = $node->childNodes->item($i);
         $v = $this->domnode_to_array($child);
         if(isset($child->tagName)) {
           $t = $child->tagName;
           if(!isset($output[$t])) {
            $output[$t] = array();
           }
           $output[$t][] = $v;
         }
         elseif($v) {
          $output = (string) $v;
         }
        }
        if(is_array($output)) {
         if($node->attributes->length) {
          $a = array();
          foreach($node->attributes as $attrName => $attrNode) {
           $a[$attrName] = (string) $attrNode->value;
          }
          $output['@attributes'] = $a;
         }
         foreach ($output as $t => $v) {
          if(is_array($v) && count($v)==1 && $t!='@attributes') {
           $output[$t] = $v[0];
          }
         }
        }
       break;
      }
      return $output;
    }
}